com.google.common.base
字符串处理
Guava 把字符串处理动作分了几大类,每种动作都有对应的工具类实现,我们可以根据需要使用对应的工具类。
CharMatcher
com.google.common.base.CharMatcher 是 Guava 提供的用于进行字符匹配的工具类,翻开 CharMatcher 的源码,我们知道 CharMatcher 是一个抽象类,在其内部 Guava 做了大量默认实现,用来更方便的对字符串做匹配,并通过构造者模式对匹配后的字符串进行处理。
Note:不支持正则表达式。
Joiner
com.google.common.base.Joiner 用来拼接字符串,可以避免大量的手动拼接 appendTo() 方法,Joiner 可以实现 Iterable<?>、Object[]、Map<?, ?> 类型的拼接。但要实现基本类型数组的拼接就无能为力了,这时就需要借助 com.google.common.base.primitives 包的基本类型工具类来实现了。
Joiner 底层通过 StringBuilder 实现,非线程安全。
Splitter
com.google.common.base.Splitter 用来分割字符串,可以方便的以任意字符分割字符串,并提供转换为 Map 的方法 MapSplitter.withKeyValueSeparator(String separator)。
Note:支持正则表达式分割字符串。
Strings
com.google.common.base.Strings 的功能较少,Guava 提供的其它几个工具类已基本可以实现字符串处理的相关功能。
Charsets
字符串编码一直是我们很头疼的事情,相信我们都写过这样一行代码:
1 | String s = "trang"; |
这样写有很多缺点,首先我们的大脑得记住常用的字符串编码,不是 UTF_,不是 UTF+,只能是 UTF- 或者 UTF,其次错误输入后的后果也很严重,JVM 会抛出 java.nio.charset.UnsupportedCharsetException 异常。
com.google.common.base.Charsets 给我们提供了一种便利的方式,Charsets 类提供了常见的 Charset 编码集,给我们的大脑腾出了位置并且避免了异常。
Note:Java中提供了类似功能的 java.nio.charset.StandardCharsets类。
CaseFormat
com.google.common.base.CaseFormat 很机智的替我们解决的大小写转换的问题,并且提供了额外的内容。
在处理数据库与 POJO 的映射时,该类有奇效。
| 类型 | 说明 | 示例 |
|---|---|---|
| LOWER_CAMEL | 小写驼峰 | lowerCamel |
| LOWER_HYPHEN | 小写连接符 | lower-hyphen |
| LOWER_UNDERSCORE | 小写下划线 | lower_underscore |
| UPPER_CAMEL | 大写驼峰 | UpperCamel |
| UPPER_UNDERSCORE | 大写下划线 | UPPER_UNDERSCORE |
函数式编程
在 Java8 面世之前,Guava 一直是函数式编程的不二之选,但过度使用 Guava 函数式编程会导致冗长、混乱、可读性差而且低效的代码。这是迄今为止最容易(也是最经常)被滥用的部分,如果你想通过函数式风格达成一行代码,致使这行代码长到荒唐,Guava 团队会泪流满面。
Predicate 和 Function 是函数式编程中最重要的两个接口,通常通过匿名内部类的方式实现自己的函数,也可以通过对应的工具类使用 Guava 已为你写好的函数。
Predicate
com.google.common.base.Predicate<T>,断言预期结果,如果与预期不符则放弃。Predicate 只声明了一个方法 boolean apply(T input) ,使用时只需要实现断言表达式即可。
CharMatcher 和 Range 也是通过 Predicate 实现的。
常见使用 Predicate 的方法:
1 | Iterables.filter(Iterable<T> unfiltered, Predicate<? super T> predicate) |
Function
com.google.common.base.Function<F, T>,函数,Function 最常用的功能是转换集合,同样只需要实现 T apply(F input) 即可愉快的玩耍了。
常见使用 Function 的方法:
1 | 与`Predicate`基本一致 |
Supplier
com.google.common.base.Supplier<T> 可以对传入的对象进行包装构建后再输出。与前两个函数接口一样,Supplier 只提供了一个供实现的方法 T get(),用于获取包装后的对象。由于 Supplier 在对象的外层,所以 Supplier 的一个重要作用是赋予对象懒加载的特性。
其它工具类
Optional
Guava 用 com.google.common.base.Optional 表示可能为null的T类型引用。一个 Optional 实例可能包含非 null 的引用(我们称之为引用存在),也可能什么也不包括(称之为引用缺失)。它从不说包含的是 null 值,而是用存在或缺失来表示。但 Optional 从不会包含 null 值引用。
Optional 最大的优点在于它是一种傻瓜式的防护。Optional 迫使你积极思考引用缺失的情况,因为你必须显式地从 Optional 获取引用。
Stopwatch
com.google.common.base.Stopwatch是一种灵活的代替System.currentTimeMillis()和System.nanoTime()的方式。
你可以把Stopwatch想象成一个秒表,它支持暂停和重置,并且支持java.util.concurrent.TimeUnit的任何计时单位。
com.google.common.collect
拓展集合
Guava对Java默认的集合做了大量拓展,以实现不同的业务需求。
ImmutabelMap
Guava的Immutable系列被很多人推崇,Immutable对象的数据在创建时提供,并且在整个生命周期内不可变,这样带来了一些好处:
线程安全
节省空间,有效利用内存
可当做常量使用以前我们常使用Collections.unmodifiableXxx()来定义常量集合,但我们都知道它是有缺陷的。以后当我们使用常量集合时,推荐大家使用Guava的Immutable集合,Guava把所有集合类都建立了对应的不可变集合。
常见ImmutabelMap实现类:
| 可变集合类型 | 来源 | Guava不可变集合 |
|---|---|---|
| Collection | JDK | ImmutableCollection |
| List | JDK | ImmutableList |
| Set | JDK | ImmutableSet |
| SortedSet | JDK | ImmutableSortedSet |
| Map | JDK | ImmutableMap |
| SortedMap | JDK | ImmutableSortedMap |
| Multiset | Guava | ImmutableMultiset |
| SortedMultiset | Guava | ImmutableSortedMultiset |
| Multimap | Guava | ImmutableMultimap |
| ListMultimap | Guava | ImmutableListMultimap |
| SetMultimap | Guava | ImmutableSetMultimap |
| BiMap | Guava | ImmutableBiMap |
| ClassToInstanceMap | Guava | ImmutableClassToInstanceMap |
| TypeToInstanceMap | Guava | ImmutableTypeToInstanceMap |
| Table | Guava | ImmutableTable |
| RangeSet | Guava | ImmutableRangeSet |
| RangeMap | Guava | ImmutableRangeMap |
Note:
当进行add、remove等操作时抛出java.lang.UnsupportedOperationException,该异常为运行时异常,并不会在编译时提醒你,需要开发时注意
所有ImmutableMap均不支持null值
所有ImmutableMap均不支持插入相同的key
MultiSet
com.google.common.collect.Multiset<E>和Set的区别是可以保存多个相同的对象。在JDK中,List和Set有一个基本的区别,就是List有序且可重复,而Set不能有重复,也不保证顺序(有些实现有顺序,例如LinkedHashSet和SortedSet等)所以Multiset占据了List和Set之间的一个灰色地带:允许重复,但不保证顺序。
常见使用场景:Multiset有一个有用的功能,就是跟踪每种对象的数量,所以你可以用来进行数字统计。
1 | HashMultiset.<Integer>create().count(element); |
常见MultiSet实现类:
| Value类型 | 来源 | Gauva Multimap |
|---|---|---|
| Enum | JDK | EnumMultiset |
| HashMap | JDK | HashSetMultiset |
| LinkedHashMap | JDK | LinkedHashMultiset |
| TreeMap | JDK | TreeMultiset |
| ConcurrentHashMap | JDK | ConcurrentHashMultiset |
| ImmutableMap | Guava | ImmutableMultiset |
MultiMap
Guava中提供了形如Map<K, List<V>>或者Map<K, Set<V>>的新集合com.google.common.collect.Multimap<K, V>,方便的实现了把一个键对应到多个值的数据结构。
常见MultiMap实现类:
| Value类型 | 来源 | Gauva Multimap |
|---|---|---|
| ArrayList | JDK | ArrayListMultimap |
| LinkedList | JDK | LinkedListMultimap |
| HashSet | JDK | HashMultimap |
| LinkedHashSet | JDK | LinkedHashMultimap |
| TreeSet | JDK | TreeMultimap |
| ImmutableList | Guava | ImmutableListMultimap |
| ImmutableSet | Guava | ImmutableSetMultimap |
Note:MultiMap并不是Map
除了两个ImmutableMap,其它均支持null键和null值
BiMap
com.google.common.collect.BiMap<K, V>是一个双向Map,在BiMap中,键值都是唯一的。
常见BiMap实现类:
| Value类型 | 来源 | Gauva Multimap |
|---|---|---|
| HashMap | JDK | HashBiMap |
| EnumMap | JDK | EnumBiMap |
| EnumMap | JDK | EnumHashBiMap |
| ImmutableMap | Guava | ImmutableBiMap |
Table
com.google.common.collect.Table<R, C, V>代替了形如Map<FirstName, Map<LastName, Person>>的集合,通过行和列来确定唯一的值。
常见Table实现类:
| Value类型 | 来源 | Gauva Multimap |
|---|---|---|
| HashMap | JDK | HashBasedTable |
| TreeMap | JDK | TreeBasedTable |
| ImmutableMap | Guava | ImmutableTable |
| Guava | ArrayTable |
ClassToInstanceMap
com.google.common.collect.ClassToInstanceMap<B>是一种特殊的Map:它的键是类型,而值是符合键所指类型的对象。
Guava提供了两种有用的ClassToInstanceMap实现:com.google.common.collect.MutableClassToInstanceMap和 com.google.common.collect.ImmutableClassToInstanceMap。
为了扩展Map接口,ClassToInstanceMap额外声明了两个方法:T getInstance(Class<T>) 和T putInstance(Class<T>, T),从而避免强制类型转换,同时保证了类型安全。
1 | ClassToInstanceMap<Object> map = MutableClassToInstanceMap.create(); |
Note:通常泛型为java.lang.Object
Range
1 |
|
集合工具类
Guava对JDK内置和Guava拓展的集合均开发了工具类,分别为Collections、Iterables、Lists、Sets、Maps、Queues、Multisets、Multimaps、Tables,里面囊括了异常强大的静态工具方法。
Guava对拓展的具体集合实现类没有提供基于工具类的初始化方法,而是直接在集合类中提供了静态工厂方法。
Collections
com.google.common.collect.Collections提供的方法不多,最常用的方法是函数编程的两个方法,具体内容在 函数式编程:
1 | Collection<E> filter(Collection<E> unfiltered, Predicate<? super E> predicate) |
Iterables
com.google.common.collect.Iterables为所有实现java.lang.Iterable<T>接口的类提供了大量实用方法。如果你使用了Iterator,Guava同样为你提供了Iterators,它们的作用基本一致。
Iterables并不会傻瓜式的任何方法都会遍历对象,而是很精明的通过instanceof判断对象实际的类型,如果匹配上则调用该类型的方法,匹配不上才会遍历。
Note:建议用Iterables代替Collections
Lists
com.google.common.collect.Lists提供了创建List的工厂方法,其余有用的有两个:
1 | List<List<B>> cartesianProduct(List<? extends List<? extends B>> lists) |
Lists没有函数编程的filter()方法,需要使用工厂方法代替实现:
1 | Lists.newArrayList(Iterables.filter(from, Predicates.contains(Pattern.compile("[-]")))); |
Sets
由于Set的不重复特性,我们常用Set实现一些算法,而com.google.common.collect.Sets贴合实际的满足了我们的要求,提供了交集、并集、差集等多种运算方式,并定义了视图com.google.common.collect.Sets.SetView来展示结果。
1 | SetView<E> intersection(final Set<E> set, final Set<?> set) |
MultiSet的工具类为com.google.common.collect.MultiSet。
Maps
com.google.common.collect.Maps提供了Map、SortedMap、BiMap的工厂方法及工具,Multimap的工具类为com.google.common.collect.Multimaps。
Maps中比较常用的方法是ImmutableMap<K, V> uniqueIndex(Iterable<V> values, Function<? super V, K> keyFunction)。
比较器
ComparisonChain
在Java中,我们实现排序往往有两种方式:
要排序的对象实现`java.lang.Comparable<T>`接口,重写`compareTo(T o)`方法
定义排序对象,实现`java.util.Comparator<T>`接口,重写`compare(T o, T o)`方法重写比较方法是件麻烦的事情,Guava又一次帮我们逃离苦海,利用com.google.common.collect.ComparisonChain轻松愉快的完成比较方法。
1 | public int compare(Cut cut, Cut cut) { |
Ordering
ComparisonChain带来的功能仍然比较单一,而Guava同时为我们提供了异常强大且方便的链式调用比较器com.google.common.collect.Ordering<T>,Ordering实现了Comparator接口,所以完全可以用Ordering替代Comparator。
Ordering提供了大量的默认实现,每个比较器都提供了常见的链式调用方法,大家可以根据实际情况创建自己的比较器。
Note:
基本类型的比较可以使用com.google.common.base.primitives包
Java中提供了类似功能的java.util.Comparators类
com.google.common.cache
缓存是一个成熟的系统中必不可少的一环,合理利用缓存可以显著提升系统响应速度,减少I/O压力,Java常见的缓存有Redis、Memcached、EhCache等,而今天我们介绍的是Guava提供的本地缓存Guava Cache。
Guava Cache在很多场景下都是相当有用的,比如初始化查找树,我们只需要对不同的树初始化一次,以后直接调用即可。
Guava Cache与ConcurrentMap很相似,但不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。
应用缓存
Cache
com.google.common.cache.Cache<K, V>是Guava Cache的基本接口,Guava为我们提供了一个默认实现LocalManualCache,我们可以通过CacheBuilder工具类的工厂方法来创建LocalManualCache对象。
1 | CacheBuilder.newBuilder().build() |
CacheLoader
在使用Guava Cache前,首先问自己一个问题:有没有合理的默认方法来加载与键关联的值?如果没有,Cache就是为你打造的,你需要在获取缓存的值时传入一个Callable实例来保证Guava Cache的作用。如果有,那么CacheLoader更适合你,而这也是今天的重点。
com.google.common.cache.LoadingCache<K, V>是实现了CacheLoader的Guava Cache,与创建Cache实例时仅有一点不同,只需要调用build()的重载方法即可。
1 | CacheBuilder.newBuilder().build(cacheLoader); |
正因为LoadingCache有了默认的加载方法,所以只需要调用get(K key)即可得到值。
自定义缓存
CacheBuilder
从之前的代码中大家都看到了,Guava Cache的实例正是通过CacheBuilder创建的,事实上,CacheBuilder的作用远远不止这些,掌握好CacheBuilder,享受自定义Guava Cache的乐趣吧。
自定义CacheBuilder参数:
| 方法 | 参数 | 说明 |
|---|---|---|
| initialCapacity | int | 初始化容量 |
| maximumSize | long | 最大容量 |
| weigher | Weigher | 权重函数 |
| maximumWeight | long | 最大权重 |
| concurrencyLevel | int | 并发级别 |
| expireAfterAccess | long, TimeUnit | 上次访问给定时间后回收 |
| expireAfterWrite | long, TimeUnit | 缓存写入给定时间后回收 |
| refreshAfterWrite | long, TimeUnit | 缓存写入给定时间后更新 |
| removalListener | RemovalListener | 移除监听器(同步) |
| recordStats | 统计状态 |
CacheBuilder提供了一系列的参数供我们个性化,主要是为了缓存的回收。CacheBuilder创建的实例并不会自动清理失效的缓存,而是在你进行读或写操作的时候顺带维护。这样做的原因在于,如果要自动地持续清理缓存,就必须有一个线程,这个线程会和用户操作竞争共享锁。
com.google.common.net
常用类
@Beta: HostAndPort HostSpecifier InetAddresses InternetDomainName MediaType PercentEscaper UrlEscapers
@Release: HttpHeaders
Guava中的com.google.common.net包目前提供的功能较少,而且大多类都标注了@Beta的注解,在Guava中标记@Beta表示这个类还不稳定,有可能在以后的版本中变化,或者去掉,所以不建议大量使用,这里也是只做简单的介绍。
HttpHeaders
先介绍下唯一一个没有@Beta注解的类HttpHeaders,这个类中并没有实质的方法,只是定义了一些Http头名称的常量,通常如果需要我们会自己定义这些常量,如果你引用了Guava包,那么就不再建议我们自己定义这些头名称的常量了,直接用它定义的即可。
这里面应该有几乎所有的Http头名称,例如:X_FORWARDED_FOR,CONTENT_TYPE,ACCEPT等,用法也没有必要介绍了,直接引用常量就可以了。
HostAndPort
有时候我们需要得到请求的ip,这时候通畅需要自己写方法解析url。而Guava给我们提供的HostAndPort类正是做这种事情的利器,可以从字符串中得到ip和port
1 | HostAndPort.fromString(String hostPortString) |